﻿#if UNITY_2019_4_OR_NEWER
#define UNITY_3D
#endif

#if UNITY_3D && (UNITY_EDITOR || COMMAND_PROFILER)
#define PROFILING
#endif

using System.Collections;
using System.Collections.Generic;
#if UNITY_3D
using UnityEngine;
#endif
#if PROFILING
using UnityEngine.Profiling;
#endif

namespace Devil
{
    using IteratorStack = Stack<IEnumerator>;

    public partial class ParallelDispatcher
    {

        IteratorStack GetIteratorStack()
        {
            return mStackPool.Get();
        }

        void ReleaseStack(IteratorStack stack)
        {
            stack.Clear();
            mStackPool.Add(stack);
        }

        public enum ExecuteThread : byte
        {
            MainThread,
            SubThread,
            Parallel,
        }

        internal abstract class BaseCommand : IYieldInstruction
        {
            internal readonly int id;
            internal readonly BaseCommand dependentOn;
            //protected bool _complete;
            internal bool isComplete; //{ get { return _complete; } }
            internal abstract ExecuteThread executeThread { get; }
            internal BaseCommand(int id, BaseCommand dependent)
            {
                this.id = id;
                this.dependentOn = dependent;
            }
            public override int GetHashCode()
            {
                return id;
            }
            internal abstract bool IsCommand(object cmd);
            internal virtual bool IsReady { get { return dependentOn == null || dependentOn.isComplete; } }
            internal abstract float GetExecutionProgress(ParallelDispatcher threading);
            internal abstract void Schedule(ParallelDispatcher threading);
            internal abstract void Execute(IThreadWorker worker);
            internal abstract void OnBreak();
            public bool YieldContinue()
            {
                return isComplete;
            }

#if PROFILING
            bool _started;
            long _startTime;
            long _waitTime;
            internal void BeginSample()
            {
                if(!_started)
                {
                    if(isComplete)
                    {
                        Debug.LogFormat("[Parallel] \"{0}\" was already complete.", ToString());
                        return;
                    }
                    var t = System.DateTime.UtcNow.Ticks;
                    _startTime = t;
                    _waitTime = t;
                    _started = true;
                }
            }

            protected void ScheduleSample()
            {
                if (_started)
                {
                    _waitTime = System.DateTime.UtcNow.Ticks;
                }
            }

            internal void EndSample(bool abort)
            {
                if (_started)
                {
                    _started = false;
                    var t = System.DateTime.UtcNow.Ticks;
                    if (abort)
                        Debug.LogFormat("[Parallel] \"{0}\" was abort. (total time: {1:0.####}s, wait time: {2:0.####}s)",
                            ToString(), (t - _startTime) * tick_2_seconds, (t - _waitTime) * tick_2_seconds);
                    else
                        Debug.LogFormat("[Parallel] \"{0}\" was complete. (total time: {1:0.####}s, wait time: {2:0.####}s)",
                            ToString(), (t - _startTime) * tick_2_seconds, (t - _waitTime) * tick_2_seconds);
                }
            }
#endif
        }

        private class SubCmd : BaseCommand
        {
            //SubThreading mSchedule;
            ICommand mCmd;
            ExecuteThread mExecThread;
#if PROFILING
            string desc;
            Object context;
#endif

            internal override ExecuteThread executeThread { get { return mExecThread; } }
            internal override bool IsCommand(object cmd)
            {
                return mCmd.Equals(cmd);
            }
            internal SubCmd(int id, bool mainThread, ICommand cmd, BaseCommand dependent) : base(id, dependent)
            {
                mCmd = cmd;
                mExecThread = mainThread ? ExecuteThread.MainThread : ExecuteThread.SubThread;
#if PROFILING
                try
                {
                    if (cmd is IDebugContext)
                        context = ((IDebugContext)cmd).GetContext();
                    else
                        context = cmd as Object;
                    desc = cmd.ToString() ?? cmd.GetType().Name;
                }
                catch
                {
                    desc = cmd.GetType().Name;
                }
#endif
            }

            internal override void OnBreak()
            {
                try
                {
                    if (mCmd is IBreakCommandSignal)
                        ((IBreakCommandSignal)mCmd).OnBreak();
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
#if PROFILING
                EndSample(true);
#endif
            }

            internal override float GetExecutionProgress(ParallelDispatcher threading)
            {
                return isComplete ? 1f : (mCmd is IExecutableProgress ? ((IExecutableProgress)mCmd).Progress : 0.5f);
            }

            internal override void Schedule(ParallelDispatcher schedule)
            {
                //mSchedule = schedule;
                lock (schedule.mLock)
                {
                    if (mExecThread == ExecuteThread.MainThread)
                    {
                        schedule.mMainCmdQueue.Add(this);
                    }
                    else
                    {
                        schedule.mSubCmdQueue.Add(this);
                        schedule.StartWorker();
                    }
                }
#if PROFILING
                ScheduleSample();
#endif
                //worker.mExecuting = this;
                //ThreadPool.QueueUserWorkItem(mSchedule.mSubExecutor, this);
            }

            internal override void Execute(IThreadWorker worker)
            {
#if PROFILING
                Profiler.BeginSample(desc/*, context*/);
#endif
                try
                {
                    //threadId = worker.threadId;
                    if (!isComplete)
                        mCmd.Execute();
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e
#if PROFILING
                        , context
#endif
                        );
                }
                //finally
                //{
                //    threadId = 0;
                //}
                isComplete = true;
#if PROFILING
                Profiler.EndSample();
                EndSample(false);
#endif
            }

            public override string ToString()
            {
                return mCmd.ToString();
            }
        }

        private class ParllelIndex : BaseCommand
        {

            ICommandParallelFor<int> mCmd;
            int mSize;
            int mExecIndex;
            int mExecCount;
            int mBatchNum;
#if PROFILING
            string desc;
            Object context;
            //long start_time;
#endif
            internal override ExecuteThread executeThread { get { return ExecuteThread.Parallel; } }
            internal override bool IsCommand(object cmd)
            {
                return mCmd.Equals(cmd);
            }
            internal ParllelIndex(int id, int count, ICommandParallelFor<int> cmd, BaseCommand dependent) : base(id, dependent)
            {
                mCmd = cmd;
                mSize = count;
#if PROFILING
                try
                {
                    if (cmd is IDebugContext)
                        context = ((IDebugContext)cmd).GetContext();
                    else
                        context = cmd as Object;
                    desc = cmd.ToString() ?? cmd.GetType().Name;
                }
                catch
                {
                    desc = cmd.GetType().Name;
                }
#endif
            }

            internal override void OnBreak()
            {
                try
                {
                    if (mCmd is IBreakCommandSignal)
                        ((IBreakCommandSignal)mCmd).OnBreak();
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
#if PROFILING
                EndSample(true);
#endif
            }

            internal override float GetExecutionProgress(ParallelDispatcher threading)
            {
                return isComplete ? 1f : (mSize > 0 ? (float)mExecCount / mSize : 0f);
            }

            internal override void Schedule(ParallelDispatcher schedule)
            {
#if PROFILING
                ScheduleSample();
                //start_time = System.DateTime.UtcNow.Ticks;
#endif
                isComplete = mSize <= 0;
                if (isComplete)
                    return;
                mBatchNum = ParallelUtils.Max(2, (mSize - 1) / schedule.mWorkers.Length + 1);
                mExecCount = 0;
                mExecIndex = 0;
                //mSchedule = schedule;
                lock (schedule.mLock)
                {
                    schedule.mSubCmdQueue.Add(this);
                    var len = ParallelUtils.Min(schedule.mWorkers.Length - schedule.mActiveWorkers, ParallelUtils.Max(1, mSize >> 1));
                    while (len > 0 && schedule.StartWorker() != null)
                    {
                        len--;
                    }
                }
                //worker.mExecuting = this;
                //ThreadPool.QueueUserWorkItem(mSchedule.mSubExecutor, this);
            }

            internal override void Execute(IThreadWorker worker)
            {
                //bool finish = false;
#if PROFILING
                Profiler.BeginSample(desc/*, context*/);
#endif
                try
                {
                    //threadId = worker.threadId;
                    int starti, count;
                    lock (this)
                    {
                        starti = mExecIndex;
                        count = ParallelUtils.Min(mBatchNum, mSize - starti);
                        mExecIndex += count;
                    }
                    for (int i = 0; !isComplete && i < count; i++)
                    {
                        mCmd.Execute(i + starti);
                    }
                    lock (this)
                    {
                        mExecCount += count;
                        if (mExecCount >= mSize)
                        {
                            //finish = !isComplete;
                            isComplete = true;
                        }
                    }
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e
#if PROFILING
                        , context
#endif
                        );
                    //finish = !isComplete;
                    isComplete = true;
                }

                //if (finish && mCmd is ICompleteParallelFor)
                //    ((ICompleteParallelFor)mCmd).OnParallelForComplete();
#if PROFILING
                Profiler.EndSample();
                if (isComplete)
                    EndSample(false);
                // if (finish)
                //     Debug.LogFormat("Complete \"{0}\" (time: {1:0.######}sec, batch: {2}/{3})",
                //         desc, (System.DateTime.UtcNow.Ticks - start_time) * tick_2_seconds, mBatchNum, mSize);
#endif
            }

            public override string ToString()
            {
                return mCmd.ToString();
            }
        }

        private class ParallelCmd<T> : BaseCommand
        {
            //SubThreading mSchedule;
            ICommandParallelFor<T> mCmd;
            IEnumerable mEnumerable;
            IList<T> mList;
            int mExecIndex;
            int mExecCount;
            int mSize;
            int mBatchNum;
#if PROFILING
            string desc;
            Object context;
            //long start_time;
#endif

            internal override ExecuteThread executeThread { get { return ExecuteThread.Parallel; } }
            internal override bool IsCommand(object cmd)
            {
                return mCmd.Equals(cmd);
            }
            internal ParallelCmd(int id, ICommandParallelFor<T> cmd, IEnumerable enumerable, BaseCommand dependent) : base(id, dependent)
            {
                mCmd = cmd;
                mEnumerable = enumerable;
#if PROFILING
                try
                {
                    if (cmd is IDebugContext)
                        context = ((IDebugContext)cmd).GetContext();
                    else
                        context = cmd as Object;
                    desc = cmd.ToString() ?? cmd.GetType().Name;
                }
                catch
                {
                    desc = cmd.GetType().Name;
                }
#endif
            }

            internal override void OnBreak()
            {
                try
                {
                    if (mCmd is IBreakCommandSignal)
                        ((IBreakCommandSignal)mCmd).OnBreak();
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
#if PROFILING
                EndSample(true);
#endif
            }

            internal override float GetExecutionProgress(ParallelDispatcher threading)
            {
                return isComplete ? 1f : (mSize > 0 ? (float)mExecCount / mSize : 0f);
            }

            internal override void Schedule(ParallelDispatcher schedule)
            {
#if PROFILING
                ScheduleSample();
                //start_time = System.DateTime.UtcNow.Ticks;
#endif
                mSize = 0;
                if (mEnumerable is IList<T>)
                {
                    mList = (IList<T>)mEnumerable;
                    mSize = mList.Count;
                }
                else
                {
                    var iter = mEnumerable.GetEnumerator();
                    if (iter != null)
                    {
                        while (iter.MoveNext())
                        {
                            mSize++;
                        }
                        iter.Reset();
                        if (mSize > 0)
                        {
                            mList = new List<T>(mSize);
                            while (iter.MoveNext())
                            {
                                mList.Add((T)iter.Current);
                            }
                        }
                    }
                }
                isComplete = mSize == 0;
                if (isComplete)
                    return;
                mBatchNum = ParallelUtils.Max(2, (mSize - 1) / schedule.mWorkers.Length + 1);
                mExecCount = 0;
                mExecIndex = 0;
                //mSchedule = schedule;
                lock (schedule.mLock)
                {
                    schedule.mSubCmdQueue.Add(this);
                    var len = ParallelUtils.Min(schedule.mWorkers.Length - schedule.mActiveWorkers, ParallelUtils.Max(1, mSize >> 1));
                    while (len > 0 && schedule.StartWorker() != null)
                    {
                        len--;
                    }
                }
                //worker.mExecuting = this;
                //ThreadPool.QueueUserWorkItem(mSchedule.mSubExecutor, this);
            }

            internal override void Execute(IThreadWorker worker)
            {
                //bool finish = false;
#if PROFILING
                Profiler.BeginSample(desc/*, context*/);
#endif
                try
                {
                    //threadId = worker.threadId;
                    int starti, count;
                    lock (this)
                    {
                        starti = mExecIndex;
                        count = ParallelUtils.Min(mBatchNum, mSize - starti);
                        mExecIndex += count;
                    }
                    for (int i = 0; !isComplete && i < count; i++)
                    {
                        mCmd.Execute(mList[i + starti]);
                    }
                    lock (this)
                    {
                        mExecCount += count;
                        if (mExecCount >= mSize)
                        {
                            //finish = !isComplete;
                            isComplete = true;
                        }
                    }
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e
#if PROFILING
                        , context
#endif
                        );

                    //finish = !isComplete;
                    isComplete = true;
                }
                //finally
                //{
                //    threadId = 0;
                //}
                //if (finish && mCmd is ICompleteParallelFor)
                //    ((ICompleteParallelFor)mCmd).OnParallelForComplete();

#if PROFILING
                Profiler.EndSample();
                if (isComplete)
                    EndSample(false);
                // if (finish)
                //     Debug.LogFormat("Complete \"{0}\" (time: {1:0.######}sec, batch: {2}/{3})",
                //         desc, (System.DateTime.UtcNow.Ticks - start_time) * tick_2_seconds, mBatchNum, mSize);
#endif
            }

            public override string ToString()
            {
                return mCmd.ToString();
            }
        }

        /// <summary>
        /// yield
        /// </summary>
        private class YieldCmd : BaseCommand
        {
            IYieldCommand mCmd;
            ParallelDispatcher mSchedule;
            ExecuteThread onThread;

            IYieldInstruction mWaitCmd;
            IExecutableProgress mWaitProgress;
            IteratorStack mStack;
            int loops;

#if PROFILING
            string desc;
            Object context;
#endif
            internal override bool IsCommand(object cmd)
            {
                return mCmd.Equals(cmd);
            }
            internal override ExecuteThread executeThread { get { return onThread; } }
            internal YieldCmd(int id, IYieldCommand cmd, bool onMainThread, BaseCommand dependent) : base(id, dependent)
            {
                mCmd = cmd;
                this.onThread = onMainThread ? ExecuteThread.MainThread : ExecuteThread.SubThread;

#if PROFILING
                try
                {
                    if (cmd is IDebugContext)
                        context = ((IDebugContext)cmd).GetContext();
                    else
                        context = cmd as Object;
                    desc = cmd.ToString() ?? cmd.GetType().Name;
                }
                catch
                {
                    desc = cmd.GetType().Name;
                }
#endif
            }

            internal override float GetExecutionProgress(ParallelDispatcher threading)
            {
                if (mCmd is IExecutableProgress)
                    return ((IExecutableProgress)mCmd).Progress;
                if (loops < 5)
                    return loops * 0.2f + (mWaitProgress == null ? 0 : mWaitProgress.Progress * 0.2f);
                return 0.8f + (mWaitProgress == null ? 0 : mWaitProgress.Progress * 0.2f);
            }

            internal override void OnBreak()
            {
                try
                {
                    if (mCmd is IBreakCommandSignal)
                        ((IBreakCommandSignal)mCmd).OnBreak();
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
#if PROFILING
                EndSample(true);
#endif
            }

            internal override void Schedule(ParallelDispatcher schedule)
            {
#if PROFILING
                ScheduleSample();
#endif
                mSchedule = schedule;
                lock (schedule.mLock)
                {
                    if (onThread == ExecuteThread.MainThread)
                    {
                        schedule.mMainCmdQueue.Add(this);
                    }
                    else
                    {
                        schedule.mSubCmdQueue.Add(this);
                        schedule.StartWorker();
                    }
                }
            }

            void GetWaitCmd(IEnumerator iter)
            {
                var v = iter.Current;
                if (v is ICommandHandler)
                {
                    var handle = ((ICommandHandler)v).GetHandler();
                    if (handle.cmd == mCmd && mCmd != null)
                        throw new System.Exception(string.Format("等待任务({0})为自身, 这将造成死锁", mCmd));
                    else if (handle.IsDependentOn(this))
                        throw new System.Exception(string.Format("等待任务({0})依赖于当前任务({1}), 这将造成死锁", handle.cmd, mCmd));
                    mWaitCmd = handle.cmd;
                    mWaitProgress = handle.cmd as IExecutableProgress;
                }
                else if (v is IYieldInstruction)
                {
                    mWaitCmd = (IYieldInstruction)v;
                    mWaitProgress = v as IExecutableProgress;
                }
                else if (v is IEnumerator)
                {
                    if (mStack.Count >= ITERATOR_STACK_SIZE)
                        throw new System.Exception(string.Format("yield action stack overflow. (stack size: {0})", ITERATOR_STACK_SIZE));
                    mStack.Push((IEnumerator)v);
                    mWaitCmd = null;
                    mWaitProgress = null;
                }
                else
                {
                    mWaitCmd = null;
                    mWaitProgress = null;
                }
            }

#if PROFILING
            int profId;
            void BeginSample(ref bool profiler)
            {
                if (!profiler && ParallelUnity.frameCount != profId)
                {
                    profId = ParallelUnity.frameCount;
                    profiler = true;
                    Profiler.BeginSample(desc, context);
                }
            }

            void EndSample(ref bool profiler)
            {
                if (profiler)
                {
                    Profiler.EndSample();
                    profiler = false;
                }
            }
#endif

            internal override void Execute(IThreadWorker worker)
            {
                //loops++;
#if PROFILING
                bool profile = false;
                //Profiler.BeginSample(desc, context);
#endif
                bool act = true;
                bool broken = false;
                try
                {
                    if (isComplete)
                    {
                        act = false;
                    }
                    else if (mWaitCmd != null && !mWaitCmd.YieldContinue())
                    {
                        act = true;
                    }
                    else if (mStack == null)
                    {
#if PROFILING
                        BeginSample(ref profile);
#endif
                        loops++;
                        var iter = mCmd.Execute();
                        if (iter == null)
                        {
                            act = false;
                        }
                        else
                        {
                            mStack = mSchedule.GetIteratorStack();
                            mStack.Push(iter);
                            GetWaitCmd(iter);
                        }
                    }
                    else if (mStack.Count > 0)
                    {
#if PROFILING
                        BeginSample(ref profile);
#endif
                        loops++;
                        var iter = mStack.Peek();
                        if (iter.MoveNext())
                        {
                            GetWaitCmd(iter);
                        }
                        else
                        {
                            mStack.Pop();
                            mWaitCmd = null;
                        }
                    }
                    else
                    {
                        mWaitCmd = null;
                        act = false;
                    }

                }
                catch (System.Exception e)
                {
                    act = false;
                    broken = true;
                    Debug.LogException(e);
                }
                if (broken)
                {
                    OnBreak();
                }
                if (!act)
                {
                    if (mStack != null)
                    {
                        mSchedule.ReleaseStack(mStack);
                        mStack = null;
                    }
                    mWaitCmd = null;
                    isComplete = true;
                }
#if PROFILING
                EndSample(ref profile);
                if (isComplete)
                    EndSample(false);
#endif
            }

            public override string ToString()
            {
                return mCmd.ToString();
            }
        }

        private class LoopCmd : BaseCommand
        {
            ILoopCommand mCmd;
            ExecuteThread mExecThread;
#if PROFILING
            string desc;
            Object context;
#endif
            internal LoopCmd(int id, ILoopCommand cmd, bool mainThread, BaseCommand dependent) : base(id, dependent)
            {
                this.mCmd = cmd;
                mExecThread = mainThread ? ExecuteThread.MainThread : ExecuteThread.SubThread;
#if PROFILING
                try
                {
                    if (cmd is IDebugContext)
                        context = ((IDebugContext)cmd).GetContext();
                    else
                        context = cmd as Object;
                    desc = cmd.ToString() ?? cmd.GetType().Name;
                }
                catch
                {
                    desc = cmd.GetType().Name;
                }
#endif
            }
            internal override bool IsCommand(object cmd)
            {
                return mCmd.Equals(cmd);
            }
            internal override ExecuteThread executeThread { get { return mExecThread; } }

            internal override void Execute(IThreadWorker worker)
            {

#if PROFILING
                Profiler.BeginSample(desc/*, context*/);
#endif
                try
                {
                    //threadId = worker.threadId;
                    if (!isComplete)
                        mCmd.Execute(((ILoopWorker)worker).deltaTime);
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e
#if PROFILING
                        , context
#endif
                        );
                }
                //finally
                //{
                //    threadId = 0;
                //}
#if PROFILING
                Profiler.EndSample();
                if (isComplete)
                    EndSample(false);
#endif
            }

            internal override float GetExecutionProgress(ParallelDispatcher threading)
            {
                return isComplete ? 1f : (mCmd is IExecutableProgress ? ((IExecutableProgress)mCmd).Progress : 0.1f);
            }

            internal override void OnBreak()
            {
                try
                {
                    if (mCmd is IBreakCommandSignal)
                        ((IBreakCommandSignal)mCmd).OnBreak();
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
#if PROFILING
                EndSample(true);
#endif
            }

            internal override void Schedule(ParallelDispatcher threading)
            {
#if PROFILING
                ScheduleSample();
#endif
                lock (threading.mLock)
                {
                    if (mExecThread == ExecuteThread.MainThread)
                    {
                        threading.mMainCmdQueue.Add(this);
                    }
                    else
                    {
                        threading.mLooper.Join(this);
                        threading.StartLooperThread();
                    }
                }
            }
        }

        private class WaitCmd : BaseCommand
        {
#if PROFILING
            string mark;
#endif
            internal CascadeList<BaseCommand> mWaitTargets;
            //SubThreading mSchedule;
            internal override ExecuteThread executeThread { get { return ExecuteThread.SubThread; } }

            internal WaitCmd(int id, BaseCommand depedent) : base(id, depedent)
            {
                //mWaitTargets = new RandomList<BaseCommand>(4);
                //mLock = new object();
            }
            internal override bool IsCommand(object cmd)
            {
                return cmd == this;
            }
            internal override bool IsReady
            {
                get
                {
                    if (dependentOn != null && !dependentOn.isComplete)
                        return false;
                    lock (this)
                    {
                        if (mWaitTargets != null)
                        {
                            for (int i = mWaitTargets.Count - 1; i >= 0; i--)
                            {
                                var wait = mWaitTargets[i];
                                if (!wait.isComplete)
                                    return false;
                                mWaitTargets.RemoveAt(i);
                            }
                        }
                    }
                    return true;
                }
            }

            internal override float GetExecutionProgress(ParallelDispatcher schedule)
            {
                if (isComplete)
                    return 1f;
                else
                    return 0;
                //if (schedule == null)
                //    return 0;
                //float progress = 0;
                //int num = 0;
                //lock (schedule.mLock)
                //{
                //    schedule.GetDependentExecutionProgress(this, ref progress, ref num);
                //}
                //return num > 0 ? (progress / (float)num) : 0f;
            }

            internal override void OnBreak()
            {
#if PROFILING
                EndSample(true);
#endif
            }

            internal override void Schedule(ParallelDispatcher schedule)
            {
                isComplete = true;
#if PROFILING
                EndSample(false);
#endif
            }

            internal void Join(BaseCommand cmd)
            {
                lock (this)
                {
                    if (mWaitTargets == null)
                        mWaitTargets = new CascadeList<BaseCommand>(4);
                    mWaitTargets.Add(cmd);
                }
            }

            internal void GetAllJoinedCommands(HashSet<BaseCommand> cmds)
            {
                if (isComplete || !cmds.Add(this))
                    return;
                lock (this)
                {
                    if (dependentOn != null && !dependentOn.isComplete)
                    {
                        if (dependentOn is WaitCmd)
                            ((WaitCmd)dependentOn).GetAllJoinedCommands(cmds);
                        else
                            cmds.Add(dependentOn);
                    }
                    if (mWaitTargets != null)
                    {
                        for (int i = mWaitTargets.Count - 1; i >= 0; i--)
                        {
                            var cmd = mWaitTargets[i];
                            if (cmd.isComplete)
                                continue;
                            else if (cmd is WaitCmd)
                                ((WaitCmd)cmd).GetAllJoinedCommands(cmds);
                            else
                                cmds.Add(cmd);
                        }
                    }
                }
            }

            internal override void Execute(IThreadWorker worker)
            {
                isComplete = true;
#if PROFILING
                EndSample(false);
#endif
            }

            public void SetInfo(string info)
            {
#if PROFILING
                this.mark = string.Format("wait \"{0}\"", string.IsNullOrEmpty(info) ? "unnamed" : info);
#endif
            }

#if PROFILING
            public override string ToString()
            {
                return mark;
            }
#endif
        }

    }
}